package main

import (
	"encoding/json"
	"fmt"
)

// 定义一个结构体，类似java的类
// 结构体是值类型
type Person struct {
	Name string // 初始值为空字符串，这里不能进行赋值操作
	Age  int    // 可以没有初始值，采用系统默认值
	// 定义引用类型，没有分配内存，需要先make之后才能使用
	Scores  map[string]string // map
	Hobbys  [3]string         // 数组
	Address []string          // 切片
}

// 绑定函数(此时成为方法，因为该函数属于Person)到结构体
// 和函数调用一样，不一样的地方就是结构体方法调用的时候
// 会将结构体本身也传递进去,在改函数中可以使用结构体的一些值
// !!! 注意该方法仍然是值传递，想要使用引用传递修改为指针
func (p Person) Jump(len int) (road string) {
	fmt.Println("Jump 中的 p ", p)
	p.Age += 10
	//Jump 中的 p  {~小白 18 map[语文:100 英语:100 数学:100] [足球 篮球 羽毛球] [上海 北京 深圳]}
	return fmt.Sprintf("跳了 【%v】 米！", len)
}

func (p *Person) Jump2(len int) (road string) {
	fmt.Println("Jump2 中的 *p ", *p)
	(*p).Age += 10
	//Jump 中的 p  {~小白 18 map[语文:100 英语:100 数学:100] [足球 篮球 羽毛球] [上海 北京 深圳]}
	return fmt.Sprintf("跳了 【%v】 米！", len)
}

// JOSN 序列化配置
// 使用 tag 标签使用 反引号 括起来，tag可以通过反射机制获取
type Cat struct {
	Name string `json:"name"`
	Age  int    `json:"age"`
	Sex  string `json:"sex"`
	Addr string // 不使用 `json:xxx` 的话生成的json字符串key就保持默认
}

func test01(p Person) {
	p.Name = "~" + p.Name
}

func test02(p *Person) {
	p.Name = "~" + p.Name
}

type A001 struct {
	Name string
}

type A002 struct {
	Name string
}

func main() {

	// 1.创建结构体的四种方式
	//1.1 像 int 一样
	var p1 Person
	fmt.Println("p1", p1) //p1 { 0 map[] [  ] []}

	//1.2 初始化的时候赋值
	// var p2 = Person{"小白"} // 如果再初始化的同时赋值，必须所有的都赋值，否则都不赋值
	// var p21 = Person{} // 全部为初始值
	// fmt.Println(p21)
	var p2 Person = Person{"小白", 18, map[string]string{"数学": "100", "语文": "100", "英语": "100"}, [3]string{"足球", "篮球", "羽毛球"}, []string{"上海", "北京", "深圳"}}
	fmt.Println("p2", p2)
	//p2 {小白 18 map[数学:100 语文:100 英语:100] [足球 篮球 羽毛球] [上海 北京 深圳]}

	//1.3 使用new创建一个指针
	var p3 *Person = new(Person)
	fmt.Println("p3", p3) //p3 &{ 0 map[] [  ] []}

	//1.4 将初始化好的地址给一个指针
	var p4 *Person = &Person{}
	fmt.Println("p4", p4) //p4 &{ 0 map[] [  ] []}

	// 2 使用指针的需要注意的地方
	// 语言底层针对结构体进行了指针使用的时候的优化
	// ptr.Attr === (*ptr).Attr ,编译器会自动添加 (*ptr)
	(*p4).Name = "小刘"
	p4.Name = "小刘"

	// 测试值传递
	fmt.Println("001 - p2", p2.Name) // 小白
	test01(p2)
	fmt.Println("001 - p2", p2.Name) // 小白
	// 我们发现没有改变值，说明了struct和arr一样都是值传递
	fmt.Println("002 - p2", p2.Name) // 小白
	test02(&p2)
	fmt.Println("002 - p2", p2.Name) // ~小白
	// 我们发现通过使用指针改变了 p2 的值

	//****************************************
	// 3- 结构体调用方法
	//*****************************************
	fmt.Println("**************************************** \n 3- 结构体调用方法 \n****************************************")
	fmt.Println("p2.jump(10) = ", p2.Jump(10)) // p2.Jump(10) 等同于 (&p2).Jump(10)
	fmt.Println("p2.Age = ", p2.Age)           //p2.Age =  18，值没变，因为传递的是值拷贝
	//p2.jump(10) =  跳了 【10】 米！

	//Jump(10) //  绑定到结构体的函数只能被结构体调用
	fmt.Println("p2.jump2(10) = ", p2.Jump2(10))
	fmt.Println("p2.Age = ", p2.Age) //p2.Age =  28，值改变了，因为传递的是指针
	//p2.jump2(10) =  跳了 【10】 米！

	//****************************************
	// 4- 结构体强转
	//*****************************************
	var a001 A001
	var a002 A002
	a001 = A001(a002)
	fmt.Println("A001 and B001 :", a001, a002)
	// 可以强转，但是有要求：结构体的字段必须要求完全一样，包括字段名、类型、个数！！！
	//A001 and B001 : {} {}

	//****************************************
	// 5- JSON序列化
	//*****************************************
	var c1 Cat = Cat{"小白", 5, "男", "北大街"}
	s1, _ := json.Marshal(c1)
	fmt.Println("json序列化后的 s1 = ", string(s1))
	//json序列化后的 s1 =  {"name":"小白","age":5,"sex":"男"}
}
